
/*************************************************************************
* Copyright (c) 2008, ShenZhen Coship Electronic Ltd Co.
* All rights reserved.
* 
* File Name:		DXSoundPlay.cpp
* Summary:			implementation of the CDXSoundPlay class.
* 
* Current Version:	1.0
* Author(s):
/------------------------------------------------------------------------/
revision			author				reason					date
1.0					Ding Ning			Initialize Version		2008-6-18
/------------------------------------------------------------------------/
*************************************************************************/

#include "stdafx.h"
#include ".\DXSoundPlay.h"
#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#endif
#ifndef MAX
#define MAX(a,b)        ( (a) > (b) ? (a) : (b) )
#endif 

// #include "octstr.h"
// OCTSTR g_octaudio;
// #include "Lock.h"
// CLock g_lockaudio;

CDXSoundPlay::CDXSoundPlay(void)
{
	if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/
	{
		MessageBox(NULL, "CStreamAudio CoInitialize Failed!", "Error", MB_OK);
		return;
	}
	m_pDS = NULL ;        // DirectSound component
	m_pDSBuf = NULL ;   // Sound Buffer object
	m_pDSNotify = NULL ;  // Notification object
	m_hNotifyEvent = NULL ; 
	ZeroMemory(&m_wfxOutput, sizeof(m_wfxOutput)) ; // Wave format of output 
	m_wfxOutput.wFormatTag = WAVE_FORMAT_PCM ; 
	m_dwPlayBufSize = 0 ;  //play loop buffer size 
	m_dwNextPlayOffset = 0 ; //offset in loop buffer 
	m_dwNotifySize = 0 ;  //notify pos when loop buffer need to emit the event
	m_bPlaying = FALSE ; 
	m_audioVolume = DSBVOLUME_MAX;
}
CDXSoundPlay::~CDXSoundPlay(void)
{
	FreeDirectSound() ; 
	CoUninitialize() ; 
}
HRESULT CDXSoundPlay::InitDirectSound(HWND hWnd)
{
	if(FAILED(DirectSoundCreate(NULL, &m_pDS, NULL)))
	{
		MessageBox(NULL, "Unable to create DirectSound object", "Error", MB_OK);
		return S_FALSE;
	}
	// sets the cooperative level of the application for this sound device
	m_pDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);
	// use preset output wave format
	SetWavFormat(&m_wfxOutput) ;
//  	g_pWaveFile = new CWaveFile;
//  	g_pWaveFile->Open("G:\\abc.wav",NULL,1);
	return S_OK ; 
}
HRESULT CDXSoundPlay::FreeDirectSound()
{
	// make sure the thread gone 
	m_bPlaying = FALSE ; 
	//Sleep(500) ; //
	// stop sound play 
	if(m_pDSBuf) m_pDSBuf->Stop();

	// Release the notify event handles
	if(m_hNotifyEvent)
	{
		CloseHandle(m_hNotifyEvent) ; 
		m_hNotifyEvent = NULL ; 
	}
	// Release DirectSound objects
	SAFE_RELEASE(m_pDSBuf) ; 
	SAFE_RELEASE(m_pDS) ; 
	return S_OK ; 
}
BOOL CDXSoundPlay::Open(HWND hWnd, CircleBuf* stream_handler)
{
	HRESULT hr ; 
	m_stream_handler = stream_handler ; 
	hr = InitDirectSound(hWnd) ; 
	return (FAILED(hr)) ? FALSE : TRUE ; 
}
BOOL CDXSoundPlay::Close()
{
	HRESULT hr ; 
	hr = FreeDirectSound() ; 
	return (FAILED(hr)) ? FALSE : TRUE ; 
}
ULONG WINAPI CDXSoundPlay::notify_stream_thd(LPVOID data) 
{
	CDXSoundPlay * psmado = static_cast<CDXSoundPlay *>(data) ; 
	DWORD dwResult = 0 ; 
	DWORD Num = 0 ;
	while(psmado->m_bPlaying)
	{
		// Wait for a message
		dwResult = MsgWaitForMultipleObjects(1, &psmado->m_hNotifyEvent, 
			FALSE, INFINITE, QS_ALLEVENTS);
		// Get notification
		switch(dwResult)
		{
		case WAIT_OBJECT_0:
			{
				psmado->LoadStreamData();
			}
			break ; 
		default:
			break ; 
		}
	}
	ExitThread(0);
//	AfxEndThread(0, TRUE) ; 
	return 0 ; 
}
IDirectSoundBuffer * CDXSoundPlay::CreateStreamBuffer(IDirectSound* pDS, WAVEFORMATEX * wfx)
{
	IDirectSoundBuffer *  pDSB = NULL ;
	IDirectSoundBuffer * pDSBuffer = NULL ;
	DSBUFFERDESC dsbd;
	// calculate play buffer size 
	// Set the notification size
	m_dwNotifySize = MAX( 1024, wfx->nAvgBytesPerSec / 8 ) ;		//2000
	m_dwNotifySize -= m_dwNotifySize % wfx->nBlockAlign ;
	// Set the buffer sizes 
	m_dwPlayBufSize = m_dwNotifySize * NUM_REC_NOTIFICATIONS;		//32000

	// create the sound buffer using the header data
	ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
	dsbd.dwSize = sizeof(DSBUFFERDESC);
	// set DSBCAPS_GLOBALFOCUS to make sure event if the software lose focus could still
	// play sound as well
	dsbd.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE | DSBCAPS_GLOBALFOCUS;
	dsbd.dwBufferBytes = m_dwPlayBufSize ;
 	dsbd.lpwfxFormat = wfx ;
// 	dsbd.lpwfxFormat = g_pWaveFile->m_pwfx ;
	if(FAILED(pDS->CreateSoundBuffer(&dsbd, &pDSB, NULL)))
		return NULL;
	// get newer interface
	if(FAILED(pDSB->QueryInterface(IID_IDirectSoundBuffer, (void**)(&pDSBuffer))))
	{
		SAFE_RELEASE(pDSB) ; 
		return NULL;
	}
	// return the interface
	return pDSBuffer;
}
BOOL CDXSoundPlay::LoadStreamData() 
{
	///////////////////////
	HRESULT hr;
	VOID*   pvStreamData1    = NULL;
	DWORD   dwStreamLength1 = 0 ;
	VOID*   pvStreamData2   = NULL;
	DWORD   dwStreamLength2 = 0 ;
	DWORD   dwWritePos = 0 ;
	DWORD   dwPlayPos = 0 ;
	LONG lLockSize = 0 ;
	DWORD dwBytesWrittenToBuffer = 0;

	if( FAILED( hr = m_pDSBuf->GetCurrentPosition( &dwPlayPos, &dwWritePos ) ) )
		return S_FALSE; 
	lLockSize = dwWritePos - m_dwNextPlayOffset;
	if( lLockSize < 0 )
		lLockSize += m_dwPlayBufSize;
	// Block align lock size so that we are always write on a boundary
	lLockSize -= (lLockSize % m_dwNotifySize);
	if( lLockSize == 0 )
		return S_FALSE;
	// lock the sound buffer at position specified
	if(FAILED(m_pDSBuf->Lock( m_dwNextPlayOffset, lLockSize,
		&pvStreamData1, &dwStreamLength1, 
		&pvStreamData2, &dwStreamLength2, 0L)))
		return FALSE;
	// read in the data

// 	g_lockaudio.Lock();
// 	memcpy(pvStreamData1, g_octaudio.getBuffer(), g_octaudio.getLength());
// 	g_lockaudio.Unlock();

	if(m_stream_handler)
	{
		if (dwStreamLength1 > m_stream_handler->GetStock())
			m_stream_handler->Read(pvStreamData1, m_stream_handler->GetStock());
		else
			m_stream_handler->Read(pvStreamData1, dwStreamLength1) ;
	}

	// Move the capture offset along
	m_dwNextPlayOffset += dwStreamLength1; 
	m_dwNextPlayOffset %= m_dwPlayBufSize; // Circular buffer
	if(pvStreamData2 != NULL)
	{
		if(m_stream_handler)
		{
			if (dwStreamLength2 > m_stream_handler->GetStock())
				m_stream_handler->Read(pvStreamData2, m_stream_handler->GetStock()) ;
			else
				m_stream_handler->Read(pvStreamData2, dwStreamLength2) ; 
		}

		// Move the capture offset along
		m_dwNextPlayOffset += dwStreamLength2; 
		m_dwNextPlayOffset %= m_dwPlayBufSize; // Circular buffer
	}
	// unlock it
	if (m_pDSBuf)
		m_pDSBuf->Unlock(pvStreamData1, dwStreamLength1, pvStreamData2, dwStreamLength2) ;
	// return a success
	return TRUE;
}
BOOL CDXSoundPlay::CtrlStream(BOOL bPlaying)
{
	HRESULT hr ; 
	int i;
	m_bPlaying = bPlaying ; 
	if(m_bPlaying)
	{
		// Create a 2 second buffer to stream in wave
		m_pDSBuf = CreateStreamBuffer(m_pDS, &m_wfxOutput) ; 
		if(m_pDSBuf == NULL)
			return FALSE ; 
		// Create the notification interface
		if(FAILED(m_pDSBuf->QueryInterface(IID_IDirectSoundNotify, (void**)(&m_pDSNotify)))) 
			return FALSE ;
		// create auto notify event 
		m_hNotifyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
		// Setup the notification positions
		for( i = 0; i < NUM_REC_NOTIFICATIONS; i++ )
		{
			m_aPosNotify[i].dwOffset = (m_dwNotifySize * i) + m_dwNotifySize - 1;
			m_aPosNotify[i].hEventNotify = m_hNotifyEvent;             
		}
		// Tell DirectSound when to notify us. the notification will come in the from 
		// of signaled events that are handled in WinMain()
		if( FAILED( hr = m_pDSNotify->SetNotificationPositions( NUM_REC_NOTIFICATIONS, m_aPosNotify ) ) )
			return S_FALSE ;
		m_dwNextPlayOffset = 0 ; 

		// Fill buffer with some sound
		LoadStreamData() ; 
		// Play sound looping
		m_pDSBuf->SetCurrentPosition(0);
		m_pDSBuf->SetVolume(m_audioVolume);
		m_pDSBuf->Play(0,0,DSBPLAY_LOOPING);
		// create notify event recv thread 
//		AfxBeginThread(CStreamAudio::notify_stream_thd, (LPVOID)(this)) ;
//		g_pWaveFile->ResetFile();
		CreateThread(NULL, 0, CDXSoundPlay::notify_stream_thd, (LPVOID)(this), 0, NULL);
	}
	else
	{
		// stop play 
		// make sure the thread gone 
		Sleep(500) ; 
		// stop sound play 
		if(m_pDSBuf) m_pDSBuf->Stop();
		// Release the notify event handles
		if(m_hNotifyEvent)
		{
			CloseHandle(m_hNotifyEvent) ; 
			m_hNotifyEvent = NULL ; 
		}
		// Release DirectSound objects
		SAFE_RELEASE(m_pDSBuf) ;  
	}
	return TRUE ; 
}
BOOL CDXSoundPlay::SetWavFormat(WAVEFORMATEX * wfx)
{
	// get the default capture wave formate 
	ZeroMemory(wfx, sizeof(WAVEFORMATEX)) ; 
	wfx->wFormatTag = WAVE_FORMAT_PCM;
	// 8KHz, 16 bits PCM, Mono
	wfx->nSamplesPerSec = 8000 ; 
	wfx->wBitsPerSample = 16 ; 
	wfx->nChannels  = 1 ;
	wfx->nBlockAlign = wfx->nChannels * ( wfx->wBitsPerSample / 8 ) ;		//2
	wfx->nAvgBytesPerSec = wfx->nBlockAlign * wfx->nSamplesPerSec;			//16000
	return TRUE ; 
}
void CDXSoundPlay::SetVolume(int audioVolume)
{
	m_audioVolume = audioVolume;
	m_pDSBuf->SetVolume(m_audioVolume);
}

int CDXSoundPlay::GetVolume()
{
	return m_audioVolume;
}
